package com.yn.sample.execution;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.yn.sample.domain.*;
import com.yn.sample.execution.executor.ExecutorByOpCode;
import com.yn.sample.util.OpCodeEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.*;

@Component
@Slf4j
public class MethodExecutionEngine implements InitializingBean {
    ClassInfo classInfo;

    @Autowired
    private List<ExecutorByOpCode> executorByOpCodes;

    private Map<String, ExecutorByOpCode> executorByOpCodeMap = new HashMap<>();


    public Object execute(Object target, String methodName, ClassInfo classInfo,
                          List<Object> arguments) throws Throwable {
        this.classInfo = classInfo;

        LinkedHashMap<String, MethodInfo> methodInfoMap = this.classInfo.getMethodInfoMap();
        if (methodInfoMap != null) {
            MethodInfo methodInfo = methodInfoMap.get(methodName);

            return doExecute(target, methodInfo, this.classInfo.getConstantPoolItems(), arguments);
        }

        return null;
    }

    private Object doExecute(Object target, MethodInfo methodInfo,
                             List<ConstantPoolItem> constantPoolItems, List<Object> arguments) throws Throwable {
        List<MethodInstructionVO> instructionVOList = methodInfo.getInstructionVOList();
        /**
         * 构造next字段,将list转变为链表
         */
        assemblyInstructionList2LinkedList(instructionVOList);

        /**
         * 本地变量表,按照从javap中解析出来的：
         *     Code:
         *       stack=1, locals=4, args_size=2
         * 来创建本地变量的堆栈
         */
        Integer localVariablesSize = methodInfo.getMethodCodeStackSizeAndLocalVariablesTableSize().getLocalVariablesSize();
        List<Object> localVariables = constructLocalVariableList(target, arguments, localVariablesSize);

        /**
         * 构造指令map，方便后续跳转指令使用
         * key：指令的sequenceNum
         * value：指令
         */
        HashMap<String, MethodInstructionVO> instructionVOHashMap = new HashMap<>();
        for (MethodInstructionVO vo : instructionVOList) {
            instructionVOHashMap.put(vo.getSequenceNumber(), vo);
        }

        /**
         * 操作数栈
         */
        LinkedList<Object> operandStack = new LinkedList<>();
        MethodInstructionVO currentInstruction = instructionVOList.get(0);
        while (true) {
            ExecutorByOpCode executorByOpCode = executorByOpCodeMap.get(currentInstruction.getOpcode());
            if (executorByOpCode == null) {
                log.info("currentInstruction:{}", currentInstruction);
            }
            InstructionExecutionContext context = new InstructionExecutionContext();
            context.setTarget(target);
            context.setConstantPoolItems(constantPoolItems);
            context.setLocalVariables(localVariables);
            context.setOperandStack(operandStack);
            String desc = OpCodeEnum.getDescByNameIgnoreCase(currentInstruction.getOpcode());
            currentInstruction.setOpCodeDesc(desc);
            context.setInstructionVO(currentInstruction);

            /**
             * 如果该字节码执行后，返回值不为空，则表示，需要跳转到其他指令执行
             */
            InstructionExecutionResult instructionExecutionResult =
                    executorByOpCode.execute(context);
            log.info("after {},\noperand stack:{},\nlocal variables:{}", JSONObject.toJSONString(currentInstruction, SerializerFeature.PrettyFormat),
                    operandStack, localVariables);


            if (instructionExecutionResult == null) {
                currentInstruction = currentInstruction.getNextInstruction();
                if (currentInstruction == null) {
                    System.out.println("execute over---------------");
                    break;
                }
                continue;
            } else if (instructionExecutionResult.isReturnInstruction()) {
                return instructionExecutionResult.getResult();
            } else if (instructionExecutionResult.isExceptional()) {
                log.info("method execute over,throw exception:{}", instructionExecutionResult.getResult());
                throw (Throwable) instructionExecutionResult.getResult();
            }

                String sequenceNum = instructionExecutionResult.getInstructionSequenceNum();
            currentInstruction = instructionVOHashMap.get(sequenceNum);
            log.info("will skip to {}", currentInstruction);
        }


        return null;
    }

    private void assemblyInstructionList2LinkedList(List<MethodInstructionVO> instructionVOList) {
        // 这里排除了最后一个元素，否则会越界;最后一个元素的next为null
        for (int i = 0; i < instructionVOList.size() - 1; i++) {
            MethodInstructionVO instructionVO = instructionVOList.get(i);
            MethodInstructionVO next = instructionVOList.get(i + 1);
            instructionVO.setNextInstruction(next);
        }
    }

    private List<Object> constructLocalVariableList(Object target, List<Object> arguments, Integer localVariablesSize) {
        List<Object> localVariables = new ArrayList<>(localVariablesSize);

        /**
         * 把当前target加入到第一个slot中，也就是传说中的this
         * todo：如果是static方法，不能加这个。这个留到后续完善
         */
        localVariables.add(target);

        if (!CollectionUtils.isEmpty(arguments)) {
            localVariables.addAll(arguments);
        }
        return localVariables;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        if (executorByOpCodes != null) {
            for (ExecutorByOpCode executorByOpCode : executorByOpCodes) {
                executorByOpCodeMap.put(executorByOpCode.getOpCode().toLowerCase(), executorByOpCode);
            }

        }
    }
}
